// 3. 深冻结

function deepFreeze(object) {
  let propNames = Object.getOwnPropertyNames(object)
  for (let name of propNames) {
    let value = object[name]
    object[name] = value && typeof value === "object" ? deepFreeze(value) : value
  }
  return Object.freeze(object)
}
let person = {
  name: "Leonardo",
  profession: {
    name: "developer"
  }
}
deepFreeze(person)
person.profession.name = "doctor" // TypeError: Cannot assign to read only property
手写call()

/*
自定义函数对象的call方法
*/
export function call(fn, obj, ...args) {
  // 如果传入的是null/undefined, this指定为window
  if (obj === null || obj === undefined) {
    obj = obj || window
  }
  // 给obj添加一个方法: 属性名任意, 属性值必须当前调用call的函数对象
  obj.tempFn = fn
  // 通过obj调用这个方法
  const result = obj.tempFn(...args)
  // 删除新添加的方法
  delete obj.tempFn
  // 返回函数调用的结果
  return result
}
5.3.手写apply()
/*
自定义函数对象的apply方法
*/
export function apply(fn, obj, args) {
  // 如果传入的是null/undefined, this指定为window
  if (obj === null || obj === undefined) {
    obj = obj || window
  }
  // 给obj添加一个方法: 属性名任意, 属性值必须当前调用call的函数对象
  obj.tempFn = fn
  // 通过obj调用这个方法
  const result = obj.tempFn(...args)
  // 删除新添加的方法
  delete obj.tempFn
  // 返回函数调用的结果
  return result
}
手写bind()

import { call } from './call'
/*
 自定义函数对象的bind方法
 重要技术:
 高阶函数
 闭包
 call()
 三点运算符
*/
export function bind(fn, obj, ...args) {
  if (obj === null || obj === undefined) {
    obj = obj || window
  }
  return function (...args2) {
    call(fn, obj, ...args, ...args2)
  }
}
5.5.手写一个防抖函数
/*
实现函数防抖的函数
*/
export function debounce(callback, delay) {
  return function () {
    // console.log('debounce 事件...')
    // 保存this和arguments
    const that = this
    const args = arguments
    // 清除待执行的定时器任务
    if (callback.timeoutId) {
      clearTimeout(callback.timeoutId)
    }
    // 每隔delay的时间, 启动一个新的延迟定时器, 去准备调用callback
    callback.timeoutId = setTimeout(function () {
      callback.apply(that, args)
      // 如果定时器回调执行了, 删除标记
      delete callback.timeoutId
    }, delay)
  }
}
手写一个节流函数

/*
实现函数节流的函数
*/
export function throttle(callback, delay) {
  let start = 0 // 必须保存第一次点击立即调用
  return function () {
    // 它的this是谁就得让callback()中的this是谁, 它接收的所有实参都直接交给callback()
    console.log('throttle 事件')
    const current = Date.now()
    if (current - start > delay) { // 从第2次点击开始, 需要间隔时间超过delay
      callback.apply(this, arguments)
      start = current
    }
  }
}
手写一个深拷贝函数

/*
1). 大众乞丐版
 问题1: 函数属性会丢失
 问题2: 循环引用会出错
*/
export function deepClone1(target) {
  return JSON.parse(JSON.stringify(target))
}
/*
获取数据的类型字符串名
*/
function getType(data) {
  return Object.prototype.toString.call(data).slice(8, -1)
}
/*
2). 面试基础版本
 解决问题1: 函数属性还没丢失
*/
export function deepClone2(target) {
  const type = getType(target)
  if (type === 'Object' || type === 'Array') {
    const cloneTarget = type === 'Array' ? [] : {}
    for (const key in target) {
      if (target.hasOwnProperty(key)) {
        cloneTarget[key] = deepClone2(target[key])
      }
    }
    return cloneTarget
  } else {
    return target
  }
}
/*
3). 面试加强版本
 解决问题2: 循环引用正常
*/
export function deepClone3(target, map = new Map()) {
  const type = getType(target)
  if (type === 'Object' || type === 'Array') {
    let cloneTarget = map.get(target)
    if (cloneTarget) {
      return cloneTarget
    }
    cloneTarget = type === 'Array' ? [] : {}
    map.set(target, cloneTarget)
    for (const key in target) {
      if (target.hasOwnProperty(key)) {
        cloneTarget[key] = deepClone3(target[key], map)
      }
    }
    return cloneTarget
  } else {
    return target
  }
}
/*
4). 面试加强版本2(优化遍历性能)
 数组: while | for | forEach() 优于 for-in | keys()&forEach()
 对象: for-in 与 keys()&forEach() 差不多
*/
export function deepClone4(target, map = new Map()) {
  const type = getType(target)
  if (type === 'Object' || type === 'Array') {
    let cloneTarget = map.get(target)
    if (cloneTarget) {
      return cloneTarget
    }
    if (type === 'Array') {
      cloneTarget = []
      map.set(target, cloneTarget)
      target.forEach((item, index) => {
        cloneTarget[index] = deepClone4(item, map)
      })
    } else {
      cloneTarget = {}
      map.set(target, cloneTarget)
      Object.keys(target).forEach(key => {
        cloneTarget[key] = deepClone4(target[key], map)
      })
    }
    return cloneTarget
  } else {
    return target
  }
}
自定义instanceof工具函数

/*
自定义instanceof工具函数:
 语法: myInstanceOf(obj, Type)
 功能: 判断obj是否是Type类型的实例
 实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回true, 否则返回false
*/
export function myInstanceOf(obj, Type) {
  // 得到原型对象
  let protoObj = obj.__proto__
  // 只要原型对象存在
  while (protoObj) {
    // 如果原型对象是Type的原型对象, 返回true
    if (protoObj === Type.prototype) {
      return true
    }
    // 指定原型对象的原型对象
    protoObj = protoObj.__proto__
  }
  return false
}
自定义new工具函数

/*
自定义new工具函数
 语法: newInstance(Fn, ...args)
 功能: 创建Fn构造函数的实例对象
 实现: 创建空对象obj, 调用Fn指定this为obj, 返回obj
*/
export function newInstance(Fn, ...args) {
  // 创建一个新的对象
  const obj = {}
  // 执行构造函数
  const result = Fn.apply(obj, args) // 相当于: obj.Fn()
  // 如果构造函数执行的结果是对象, 返回这个对象
  if (result instanceof Object) {
    return result
  }
  // 如果不是, 返回新创建的对象
  obj.__proto__.constructor = Fn // 让原型对象的构造器属性指向Fn
  return obj
}
手写axios函数

/*
 1. 函数的返回值为promise, 成功的结果为response, 失败的结果为error
 2. 能处理多种类型的请求: GET/POST/PUT/DELETE
 3. 函数的参数为一个配置对象
 {
 url: '', // 请求地址
 method: '', // 请求方式GET/POST/PUT/DELETE
 params: {}, // GET/DELETE请求的query参数
 data: {}, // POST或DELETE请求的请求体参数
 }
 4. 响应json数据自动解析为js的对象/数组
*/
/* 发送任意类型请求的函数 */
function axios({
  url,
  method = 'GET',
  params = {},
  data = {}
}) {
  // 返回一个promise对象
  return new Promise((resolve, reject) => {
    // 处理method(转大写)
    method = method.toUpperCase()
    // 处理query参数(拼接到url上) id=1&xxx=abc
    /*
     {
     id: 1,
     xxx: 'abc'
     }
     */
    let queryString = ''
    Object.keys(params).forEach(key => {
      queryString += `${key}=${params[key]}&`
    })
    if (queryString) { // id=1&xxx=abc&
      // 去除最后的&
      queryString = queryString.substring(0, queryString.length - 1)
      // 接到url
      url += '?' + queryString
    }
    // 1. 执行异步ajax请求
    // 创建xhr对象
    const request = new XMLHttpRequest()
    // 打开连接(初始化请求, 没有请求)
    request.open(method, url, true)
    // 发送请求
    if (method === 'GET') {
      request.send()
    } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
      request.setRequestHeader('Content-Type', 'application/json;charset=utf-8') // 告诉服务器请求体的格式是json
      request.send(JSON.stringify(data)) // 发送json格式请求体参数
    }
    // 绑定状态改变的监听
    request.onreadystatechange = function () {
      // 如果请求没有完成, 直接结束
      if (request.readyState !== 4) {
        return
      }
      // 如果响应状态码在[200, 300)之间代表成功, 否则失败
      const { status, statusText } = request
      // 2.1. 如果请求成功了, 调用resolve()
      if (status >= 200 && status <= 299) {
        // 准备结果数据对象response
        const response = {
          data: JSON.parse(request.response),
          status,
          statusText
        }
        resolve(response)
      } else { // 2.2. 如果请求失败了, 调用reject()
        reject(new Error('request error status is ' + status))
      }
    }
  })
}
/* 发送特定请求的静态方法 */
axios.get = function (url, options) {
  return axios(Object.assign(options, { url, method: 'GET' }))
}
axios.delete = function (url, options) {
  return axios(Object.assign(options, { url, method: 'DELETE' }))
}
axios.post = function (url, data, options) {
  return axios(Object.assign(options, { url, data, method: 'POST' }))
}
axios.put = function (url, data, options) {
  return axios(Object.assign(options, { url, data, method: 'PUT' }))
}
export default axios
自定义事件总线

/*
* 自定义事件总线
*/
const eventBus = {}
/*
{
 add: [callback1, callback2]
 delete: [callback3]
}
*/
let callbacksObj = {}
/*
绑定事件监听
*/
eventBus.on = function (eventName, callback) {
  const callbacks = callbacksObj[eventName]
  if (callbacks) {
    callbacks.push(callback)
  } else {
    callbacksObj[eventName] = [callback]
  }
}
/*
分发事件
*/
eventBus.emit = function (eventName, data) {
  const callbacks = callbacksObj[eventName]
  if (callbacks && callbacks.length > 0) {
    callbacks.forEach(callback => {
      callback(data)
    })
  }
}
/*
移除事件监听
*/
eventBus.off = function (eventName) {
  if (eventName) {
    delete callbacksObj[eventName]
  } else {
    callbacksObj = {}
  }
}
export default eventBus
自定义消息订阅与发布

/*
自定义消息订阅与发布
*/
const PubSub = {}
/*
 {
 add: {
 token1: callback1,
 token2: callback2
 },
 update: {
 token3: callback3
 }
 }
*/
let callbacksObj = {} // 保存所有回调的容器
let id = 0 // 用于生成token的标记
// 1. 订阅消息
PubSub.subscribe = function (msgName, callback) {
  // 确定token
  const token = 'token_' + ++id
  // 取出当前消息对应的callbacks
  const callbacks = callbacksObj[msgName]
  if (!callbacks) {
    callbacksObj[msgName] = {
      [token]: callback
    }
  } else {
    callbacks[token] = callback
  }
  // 返回token
  return token
}
// 2. 发布异步的消息
PubSub.publish = function (msgName, data) {
  // 取出当前消息对应的callbacks
  let callbacks = callbacksObj[msgName]
  // 如果有值
  if (callbacks) {
    // callbacks = Object.assign({}, callbacks)
    // 启动定时器, 异步执行所有的回调函数
    setTimeout(() => {
      Object.values(callbacks).forEach(callback => {
        callback(data)
      })
    }, 0)
  }
}
// 3. 发布同步的消息
PubSub.publishSync = function (msgName, data) {
  // 取出当前消息对应的callbacks
  const callbacks = callbacksObj[msgName]
  // 如果有值
  if (callbacks) {
    // 立即同步执行所有的回调函数
    Object.values(callbacks).forEach(callback => {
      callback(data)
    })
  }
}
/*
4. 取消消息订阅
 1). 没有传值, flag为undefined
 2). 传入token字符串
 3). msgName字符串
*/
PubSub.unsubscribe = function (flag) {
  // 如果flag没有指定或者为null, 取消所有
  if (flag === undefined) {
    callbacksObj = {}
  } else if (typeof flag === 'string') {
    if (flag.indexOf('token_') === 0) { // flag是token
      // 找到flag对应的callbacks
      const callbacks = Object.values(callbacksObj).find(callbacks => callbacks.hasOwnProperty(flag))
      // 如果存在, 删除对应的属性
      if (callbacks) {
        delete callbacks[flag]
      }
    } else { // flag是msgName
      delete callbacksObj[flag]
    }
  } else {
    throw new Error('如果传入参数, 必须是字符串类型')
  }
}
export default PubSub
自定义数组声明式系列方法

/*
实现数组声明式处理系列工具函数
*/
/*
实现map()
*/
export function map(array, callback) {
  const arr = []
  for (let index = 0; index < array.length; index++) {
    arr.push(callback(array[index], index))
  }
  return arr
}
/*
实现reduce()
*/
export function reduce(array, callback, initValue) {
  let result = initValue
  for (let index = 0; index < array.length; index++) {
    // 调用回调函数将返回的结果赋值给result
    result = callback(result, array[index], index)
  }
  return result
}
/*
实现filter()
*/
export function filter(array, callback) {
  const arr = []
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      arr.push(array[index])
    }
  }
  return arr
}
/*
实现find()
*/
export function find(array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      return array[index]
    }
  }
  return undefined
}
/*
实现findIndex()
*/
export function findIndex(array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) {
      return index
    }
  }
  return -1
}
/*
 实现every()
 */
export function every(array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false
      return false
    }
  }
  return true
}
/*
实现some()
*/
export function some(array, callback) {
  for (let index = 0; index < array.length; index++) {
    if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true
      return true
    }
  }
  return false
}
export function test() {
  console.log('test()222')
}
手写Promise

const PENDING = 'pending' // 初始未确定的状态
const RESOLVED = 'resolved' // 成功的状态
const REJECTED = 'rejected' // 失败的状态
/*
Promise构造函数
*/
function Promise(excutor) {
  const self = this // Promise的实例对象
  self.status = PENDING // 状态属性, 初始值为pending, 代表初始未确定的状态
  self.data = undefined // 用来存储结果数据的属性, 初始值为undefined
  self.callbacks = [] // {onResolved(){}, onRejected(){}}
  /*
   将promise的状态改为成功, 指定成功的value
   */
  function resolve(value) {
    // 如果当前不是pending, 直接结束
    if (self.status !== PENDING) return
    self.status = RESOLVED // 将状态改为成功
    self.data = value // 保存成功的value
    // 异步调用所有缓存的待执行成功的回调函数
    if (self.callbacks.length > 0) {
      // 启动一个延迟时间为0的定时器, 在定时器的回调中执行所有成功的回调
      setTimeout(() => {
        self.callbacks.forEach(cbsObj => {
          cbsObj.onResolved(value)
        })
      })
    }
  }
  /*
   将promise的状态改为失败, 指定失败的reason
   */
  function reject(reason) {
    // 如果当前不是pending, 直接结束
    if (self.status !== PENDING) return
    self.status = REJECTED // 将状态改为失败
    self.data = reason // 保存reason数据
    // 异步调用所有缓存的待执行失败的回调函数
    if (self.callbacks.length > 0) {
      // 启动一个延迟时间为0的定时器, 在定时器的回调中执行所有失败的回调
      setTimeout(() => {
        self.callbacks.forEach(cbsObj => {
          cbsObj.onRejected(reason)
        })
      })
    }
  }
  // 调用excutor来启动异步任务
  try {
    excutor(resolve, reject)
  } catch (error) { // 执行器执行出错, 当前promise变为失败
    console.log('-----')
    reject(error)
  }
}
/*
用来指定成功/失败回调函数的方法
 1). 如果当前promise是resolved, 异步执行成功的回调函数onResolved
 2). 如果当前promise是rejected, 异步执行成功的回调函数onRejected
 3). 如果当前promise是pending, 保存回调函数
返回一个新的promise对象
 它的结果状态由onResolved或者onRejected执行的结果决定
 2.1). 抛出error ==> 变为rejected, 结果值为error
 2.2). 返回值不是promise ==> 变为resolved, 结果值为返回值
 2.3). 返回值是promise ===> 由这个promise的决定新的promise的结果(成功/失败)
*/
Promise.prototype.then = function (onResolved, onRejected) {
  const self = this
  onResolved = typeof onResolved === 'function' ? onResolved : value => value // 将value向下传递
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {
    throw reason
  } // 将reason向下传递
  return new Promise((resolve, reject) => { // 什么时候改变它的状态
    /*
     1. 调用指定的回调函数
     2. 根据回调执行结果来更新返回promise的状态
     */
    function handle(callback) {
      try {
        const result = callback(self.data)
        if (!(result instanceof Promise)) { // 2.2). 返回值不是promise ==> 变为resolved, 结果值为返回值
          resolve(result)
        } else { // 2.3). 返回值是promise ===> 由这个promise的决定新的promise的结果(成功/失败)
          result.then(
            value => resolve(value),
            reason => reject(reason)
          )
          // result.then(resolve, reject)
        }
      } catch (error) { // 2.1). 抛出error ==> 变为rejected, 结果值为error
        reject(error)
      }
    }
    if (self.status === RESOLVED) {
      setTimeout(() => {
        handle(onResolved)
      })
    } else if (self.status === REJECTED) {
      setTimeout(() => {
        handle(onRejected)
      })
    } else { // PENDING
      self.callbacks.push({
        onResolved(value) {
          handle(onResolved)
        },
        onRejected(reason) {
          handle(onRejected)
        }
      })
    }
  })
}
/*
用来指定失败回调函数的方法
catch是then的语法糖
*/
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected)
}
/*
用来返回一个指定vlaue的成功的promise
value可能是一个一般的值, 也可能是promise对象
*/
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    // 如果value是一个promise, 最终返回的promise的结果由value决定
    if (value instanceof Promise) {
      value.then(resolve, reject)
    } else { // value不是promise, 返回的是成功的promise, 成功的值就是value
      resolve(value)
    }
  })
}
/*
用来返回一个指定reason的失败的promise
*/
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}
/*
返回一个promise, 只有当数组中所有promise都成功才成功, 否则失败
*/
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let resolvedCount = 0 // 已经成功的数量
    const values = new Array(promises.length) // 用来保存成功promise的value值
    // 遍历所有promise, 取其对应的结果
    promises.forEach((p, index) => {
      p.then(
        value => {
          resolvedCount++
          values[index] = value
          if (resolvedCount === promises.length) { // 都成功了
            resolve(values)
          }
        },
        reason => reject(reason)
      )
    })
  })
}
/*
返回一个promise, 由第一个完成promise决定
*/
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    // 遍历所有promise, 取其对应的结果
    promises.forEach(p => {
      // 返回的promise由第一个完成p来决定其结果
      p.then(resolve, reject)
    })
  })
}
/*
返回一个延迟指定时间才成功(也可能失败)的promise
*/
Promise.resolveDelay = function (value, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 如果value是一个promise, 最终返回的promise的结果由value决定
      if (value instanceof Promise) {
        value.then(resolve, reject)
      } else { // value不是promise, 返回的是成功的promise, 成功的值就是value
        resolve(value)
      }
    }, time)
  })
}
/*
返回一个延迟指定时间才失败的promise
*/
Promise.rejectDelay = function (reason, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(reason)
    }, time)
  })
}
export default Promise
自定义数组扁平化

/*
数组扁平化: 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
 如: [1, [3, [2, 4]]] ==> [1, 3, 2, 4]
*/
/*
方法一: 递归 + reduce() + concat()
*/
export function flatten1(array) {
  return array.reduce((pre, item) => {
    if (Array.isArray(item)) {
      return pre.concat(flatten1(item))
    } else {
      return pre.concat(item)
    }
  }, [])
}
/*
方法二: ... + some() + concat()
*/
export function flatten2(array) {
  let arr = [].concat(...array)
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
